function Drag () { // drag and drop handler

  // variables for holding state of drag
  this.is = 0;
  this.isdrop = 0;
  this.id = 0;

  // adds an element to act as drag / drop object
  this.set = function(elementID, way) {
  
    if (elementID == "huddle") {
    
      element = document.getElementById("huddle").firstChild.firstChild;
    
    } else {

      element = document.getElementById(elementID);
      
    }
    
    if (way=="in") { // object can be dragged on to but not dragged out
    
      element.addEventListener("dragenter", drag.dragin);
      element.addEventListener("dragover", drag.dragin);
      element.addEventListener("drop", function(event) { drag.drop(event, this.id); });
      
    } else if (way=="out") { // object can be dragged out but not dragged on to
    
      element.addEventListener("dragstart", function(event) { drag.start(event, this.id); });
      element.addEventListener("dragend", drag.end);
    
    } else { // object can be dragged in and out
    
      element.addEventListener("dragenter", drag.dragin);
      element.addEventListener("dragover", drag.dragin);
      element.addEventListener("drop", function(event) { drag.drop(event, this.id); });
      element.addEventListener("dragstart", function(event) { drag.start(event, this.id); });
      element.addEventListener("dragend", drag.end);
    
    }

  }

  // removes drag / drop handlers from an element
  this.remove = function(elementID) {

    if (elementID == "huddle") {
    
      element = document.getElementById("huddle").firstChild.firstChild;
    
    } else {

      element = document.getElementById(elementID);
      
    }

    element.removeEventListener("dragenter", drag.dragin);
    element.removeEventListener("dragover", drag.dragin);
    element.removeEventListener("drop", function(event) { drag.drop(event, this.id); });
    element.removeEventListener("dragstart", function(event) { drag.start(event, this.id); });
    element.removeEventListener("dragend", drag.end);

  }

  // handles drag in
  this.dragin = function(event) {

    // stops anything from happening
    event.preventDefault();

  }

  // when drag out is started
  this.start = function(event, id) {
    
    this.is = 1;
    
    this.id = id;
    
    // gives the operating system something to "drop"
    event.dataTransfer.setData("applicatoin/x-vnd.adobe.air.file-list", "");
    event.dataTransfer.effectsAllowed = "none";

  }

  // handles when a drag out is ended
  this.end = function(event) {

    drag.is = 0;

    event.preventDefault();
    
    temp = sortTypeId("unknown", drag.id);
    
    if(selecter.is(temp[1])) { // if the object is selected
    
      // use the selecter download manager
      selecter.download();
    
    } else {

      // hit it straight into the download manager
      download.startDownload(drag.id);
      
    }
    
  }

  // handles when object has something dropped on it
  this.drop = function(event,id) {

    // if its a valid drop, and its not downloading / uploading already
    if ((this.isdrop == 0) && (this.is == 0) && (download.passedId == "") && (uploader.uploading == "0")) {
    
      reload = 0;

      if ((id=="") && (folder.getCurrent() != 0)) { // should it reload?
      
        id = "Folder" + folder.getCurrent();
        
        reload = 1;
      
      }

      // gets the file list
      var fileList = event.dataTransfer.getData("application/x-vnd.adobe.air.file-list");

      count = fileList.length;

      // for each file, adds it to queue
      for(var file in fileList){

        if (fileList[file].isDirectory) {
        
          // if its a directory, add all the files from the directory
          fileArray = fileList[file].getDirectoryListing();
        
          for (var directoryfile in fileArray) {
          
            if (!fileArray[directoryfile].isDirectory) {
            
              uploader.queueit(fileArray[directoryfile], id);
              
              count = count + 1;
              
            }
          
          }
        
        } else {
        
          // upload only one file
          uploader.queueit(fileList[file], id);
          
        }
      
      }

      // carry some variables over into uploader object
      uploader.count = count;
      uploader.errors = 0;
      
      uploader.reload = reload;
      
      uploader.doUploads(); // force uploader to do the upload
      
      // notification for downloads
      msgs.display("<span id='countdown'>" + count + "</span> files remaining; <span id='errors'>0</span> errors encountered<BR><span id='progress'></span>", -1);
      
      this.isdrop = 1;
    
    } else {
    
      this.is = 0;
    
    }
    
    this.isdrop = 0;   

  }
  
}

function Uploader () { // upload manager

  // initialise variables, will be reset after each upload
  this.queue = [];
  this.errors = 0;
  this.count = 0;
  this.reload = 0;
  this.uploading = 0;

  // add a file to the upload queue
  this.queueit = function(file, id) {

    temp = [];
    temp[0] = file; // file object
    temp[1] = id; // target folder id
    
    this.queue.push(temp); // add to array of files to be uploaded

  }

  // recursive function, uploads one file at a time until all done
  this.doUploads = function() {

    if (this.queue.length > 0) { // if there are still files to be uploaded
    
      this.uploading = 1; // set uploading flag
    
      temp = this.queue.pop(); // get file to upload from array

      this.uploadFile(temp[0], temp[1]); // do the upload
      
    } else { // if no more uploads left to do
      
      delay = 500;
      
      if (this.errors > 0) { // display error message
      
        msgs.setError(2, "file upload problem, " + this.errors + " errors", this.errors + " file uploads failed");
        
        delay = 4000;
      
      }
      
      if (this.reload == 1) { // reloads page if requested
      
        page.displayPage("folder", folder.getCurrent());
        
      }
      
      setTimeout(uploader.hide, delay); // end upload and reset variables
    
    }

  }

  // function to reset the uploader object
  this.hide = function() {
  
    // reset variables
    this.queue = [];
    this.errors = 0;
    this.count = 0;
    this.reload = 0;
    uploader.uploading = 0;

    msgs.unlock(); // unlock notifications for use by other parts of app

  }

  // function to upload each individual file
  this.uploadFile = function(file, id) {

    // set credentials - only works ONCE each session
    air.URLRequestDefaults.setLoginCredentialsForHost("api.huddle.net", huddle.getUsername(), huddle.getPassword());
    
    temps = [];
    temps = sortTypeId("unknown",id);
    id = temps[1];

    // form request
    var airRequest = new air.URLRequest("https://api.huddle.net/v1/json/files/upload");
     
    var variables = new air.URLVariables();
    variables.folderid = id;
    airRequest.data = variables; // add folder id to request

    airRequest.method = air.URLRequestMethod.POST;

    var fileUpload = new air.File(file.nativePath);
    
    airRequest.contentType = 'multipart/form-data';

    // event listeners to handle upload completed
    fileUpload.addEventListener(air.Event.COMPLETE, function() {
      uploader.count = uploader.count - 1;
      if (uploader.count == "") { uploader.count = "0"; }
      $("#countdown").empty().append(uploader.count); // change message
      uploader.doUploads(); // call function to do next download
      window.drop = 0;
      $("#progress").empty(); // empty progress meter
    });
    
    // error listener
    fileUpload.addEventListener(air.IOErrorEvent.IO_ERROR, function() {
      uploader.count = uploader.count - 1;
      if (uploader.count == "") { uploader.count = "0"; }
      window.uploadErrors = window.uploadErrors + 1;
      $("#countdown").empty().append(uploader.count); // current count update
      $("#errors").empty().append(window.uploadErrors); // update number of errors
      uploader.doUploads(); // try next upload anyway
      window.drop = 0;
    });
    
    // listener which is fired periodically / sporadically during upload to gauge amount complete
    fileUpload.addEventListener(air.ProgressEvent.PROGRESS, function(event) { 
      loaded = event.bytesLoaded; 
      total = event.bytesTotal;
      percentage = Math.ceil( (loaded / total) * 100 ); // calculate total complete so far
      $("#progress").empty().append(percentage + "% complete"); // write to message
      if(percentage == 100) {
        $("#progress").append(" <img src='/imgs/tick.png' id='tick'>"); // appends tick if it is complete
      }
    });

    // do the upload
    fileUpload.upload(airRequest, 'file', false); 

  }
  
}